home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume91 / news / vnres112 / part06 / std.c < prev   
C/C++ Source or Header  |  1991-03-14  |  26KB  |  1,345 lines

  1. #include <stdio.h>
  2. #include <pwd.h>
  3. #include <ctype.h>
  4.  
  5. extern char *malloc();
  6.  
  7. #ifndef    MINIX
  8. #ifndef    MSDOS
  9. #ifndef    amiga
  10. /* Minix is missing it */
  11. #include <sys/param.h>
  12. #endif
  13. #endif
  14. #endif
  15.  
  16. #include "server.h"
  17. #include "config_std.h"
  18. #include "std.h"
  19.  
  20. #ifdef    MINIX
  21. #define clearerr(p) (((p)->_flags) &= ~_ERR)
  22. #endif
  23.  
  24. #ifdef amiga
  25. extern char pr_buf[];
  26. extern ttputc ();
  27. #endif
  28.  
  29. #ifndef MAXPATHLEN
  30. #define MAXPATHLEN 240
  31. #endif
  32.  
  33. extern NODE *hashfind();
  34. #ifndef amiga
  35. extern FILE *fopen();
  36. #endif
  37. extern char *index(), *rindex();
  38. extern char *str_tstore(), *str_tpool(), *str_store();
  39. extern char *strtok(), *strpbrk();
  40. extern char *regex(), *regcmp();
  41.  
  42. #ifdef MAILCHOOSE
  43. extern int (*Massage)();
  44. #endif
  45.  
  46. /*
  47.     global flags signifying options set
  48. */
  49. #define GF_ALL 1    /* -x option - scan everything */
  50. #define GF_SPEC 2    /* -n option(s) - user specified groups */
  51. #define GF_OVER 4    /* command line specification - overide marks */
  52.  
  53. char *Vns_version = "res1.1";
  54.  
  55. static char *Onews, *Newsrc;
  56. static int Ntopt, Nntopt, Nwopt, Nnwopt;
  57.  
  58. static char *Wopt[NUMFILTER];        /* regular expressions for -w options */
  59. static char *Topt[NUMFILTER];        /* for -t options */
  60. static char *Negwopt[NUMFILTER];    /* for negated -w options */
  61. static char *Negtopt[NUMFILTER];    /* for negated -t options */
  62.  
  63. static char *Options[OPTLINES];
  64. static int Max_name, Optlines;
  65. static unsigned Gflags = 0;
  66. static char **Active;
  67. static int Actnum;
  68. static char *Mailer, *Poster;
  69.  
  70. static char *RT_head = RTHEAD;
  71. static char *P_head = PHEAD;
  72. static char *M_head = MHEAD;
  73. static char *R_head = RHEAD;
  74. static char *TO_head = TOHEAD;
  75. static char *F_head = FHEAD;
  76. static char *FT_head = FTHEAD;
  77. static char *T_head = THEAD;
  78. static char *DIS_head = DISHEAD;
  79. static char *L_head = LHEAD;
  80. static char *N_head = NHEAD;
  81.  
  82. static char *Fpfix = FPFIX;
  83.  
  84. static void specmark (), mail_cmd ();
  85.  
  86. static  g_dir (), emptyline (), fill_active (), art_active (), chkgroup (),
  87.     arg_opt (), newsrc_opt (), do_opt (), specfilter (),
  88.     findall (), digname ();
  89. static char *nfgets (), *mail_trim ();
  90.  
  91. /*
  92. **    environment setup.
  93. */
  94. vns_envir()
  95. {
  96.      char dbuf[MAXPATHLEN], *rcname;
  97.     char *vn_env();
  98.     struct passwd *ptr, *getpwuid();
  99. #ifdef amiga
  100.     long    fh;    /* on the amiga creat or open locks a file */
  101. #endif
  102. #ifdef MAILCHOOSE
  103.     int mail_prompt();
  104.  
  105.     Massage = mail_prompt;
  106. #endif
  107.  
  108.     ptr = getpwuid (getuid());
  109.  
  110.     rcname = vn_env("MAILER",DEF_MAIL);
  111. #ifdef INLETTER
  112.     sprintf(dbuf,"cat %%s | %s",rcname);
  113. #else
  114.     /* used as a format string TWICE (%%%% -> %% -> %) */
  115.     sprintf(dbuf,"cat %%%%s | %s %%s",rcname);
  116. #endif
  117.     Mailer = str_store(dbuf);
  118.     rcname = vn_env("VNPOSTER",DEF_POST);
  119.     sprintf(dbuf,"%s %%s",rcname);
  120.     Poster = str_store(dbuf);
  121.     rcname = vn_env("NEWSRC",DEF_NEWSRC);
  122.     if (*rcname != DIRSEP)
  123.     {
  124. #ifdef amiga
  125.         sprintf (dbuf, "%s%s",ptr->pw_dir,rcname);
  126. #else
  127.         sprintf (dbuf, "%s%c%s",ptr->pw_dir,DIRSEP,rcname);
  128. #endif
  129.         Newsrc = str_store (dbuf);
  130.     }
  131.     else
  132.         Newsrc = str_store (rcname);
  133.  
  134.     /* above logic guarantees that Newsrc contains a '/' */
  135.      strcpy(dbuf,Newsrc);
  136. #ifdef amiga
  137.      strcpy(rindex(dbuf,':')+1,"vnXXXXXX");
  138. #else
  139.      strcpy(rindex(dbuf,DIRSEP)+1,"vnXXXXXX");
  140. #endif
  141.     mktemp(dbuf);
  142.     Onews = str_store (dbuf);
  143.  
  144.     if (access (Newsrc,0) != 0)
  145. #ifdef amiga
  146.     { /* we must close a file we creat otherwise it is locked and
  147.          inaccesible */
  148.         fh = creat (Newsrc,0666);
  149.         if (fh > 0)
  150.             close (fh);
  151.     }
  152. #else
  153.         creat (Newsrc,0666);
  154. #endif
  155. }
  156.  
  157. /*
  158.     change directory to group
  159. */
  160. vns_gset(grp)
  161. char *grp;
  162. {
  163.     char dbuf [RECLEN];
  164.     g_dir (grp,dbuf);
  165.     if (chdir(dbuf) < 0)
  166.         printex("can't change to newsgroup directory");
  167. }
  168.  
  169. /*
  170.     g_dir converts newsgroup name to directory string
  171. */
  172. static
  173. g_dir(s,t)
  174. char *s,*t;
  175. {
  176.     char *ptr;
  177. #ifdef amiga
  178.     char *spooldir, *nested;
  179.     spooldir = vn_env("NEWSDIR",NULL);
  180.     if (spooldir == NULL)
  181.         sprintf (t,"%s%s",SPOOLDIR,s);
  182.     else
  183.         sprintf (t,"%s%s",spooldir,s);
  184. #else
  185.     sprintf (t,"%s%c%s",SPOOLDIR,DIRSEP,s);
  186. #endif
  187. #ifdef    amiga
  188. /* if AmigaUUCP and VNNESTED not defined or is "N" do not replace .'s
  189.    in groupnames */
  190.     nested = vn_env("VNNESTED",NULL);
  191.     if (nested != NULL && *nested != 'N')
  192. #endif
  193.     for (ptr=t+strlen(SPOOLDIR)+1;
  194.          (ptr = index(ptr,'.')) != NULL;
  195.          *ptr = DIRSEP)
  196.         ;
  197. }
  198.  
  199. /*
  200. ** myfind is used for hashfind() calls which aren't supposed to fail.
  201. */
  202. static NODE *
  203. myfind(name)
  204. char *name;
  205. {
  206.     NODE *n;
  207.  
  208.     n = hashfind(name);
  209.     if (n == NULL)
  210.         printex("Unexpected table lookup failure");
  211.     return (n);
  212. }
  213.  
  214. vns_news(argc,argv,lfirst,nun)
  215. int argc;
  216. char **argv;
  217. int *lfirst, *nun;
  218. {
  219.     FILE *fp;
  220.     static char marks[] =
  221.     { 
  222.         NEWS_ON, NEWS_OFF, '\0' 
  223.     };
  224.     int line, len, num;
  225.     char buf [RECLEN], trail, optpflag, submark, *fret, *ptr;
  226.  
  227.     ++argv;
  228.     --argc;
  229.  
  230.     /* fill table with active newsgroups */
  231.     fill_active ();
  232.  
  233.     if (argc > 0)
  234.     {
  235.         Gflags |= GF_OVER;
  236.         arg_opt(argc,argv,lfirst,nun);
  237.         optpflag = 'y';
  238.     }
  239.     else
  240.         optpflag = 'n';
  241.  
  242.     if ((fp = fopen (Newsrc,"r")) == NULL)
  243.         printex ("can't open %s for reading",Newsrc);
  244.  
  245.     Optlines = 0;
  246.  
  247.     for (line = 1; (fret = fgets(buf,RECLEN-1,fp)) != NULL && emptyline(buf) == 1; ++line)
  248.         ;
  249.     if (fret != NULL && strncmp (buf,"options",7) == 0)
  250.     {
  251.         Options[0] = str_store(buf);
  252.         Optlines = 1;
  253.         trail = buf [strlen(buf)-2];
  254.         for ( ; (fret = fgets(buf,RECLEN-1,fp)) != NULL; ++line)
  255.         {
  256.             if (trail != '\\' && buf[0] != ' ' && buf[0] != '\t')
  257.                 break;
  258.             if (Optlines >= OPTLINES)
  259.                 printex ("%s - too many option lines (%d allowed)",Newsrc,OPTLINES);
  260.             Options[Optlines] = str_store(buf);
  261.             ++Optlines;
  262.             if ((len = strlen(buf)) >= 2 && buf[len-2] != '\\')
  263.                 trail = buf[len-2];
  264.             else
  265.                 trail = '\0';
  266.         }
  267.     }
  268.  
  269.     /* do the options from the newsrc file if there weren't command line args */
  270.     if (Optlines > 0 && optpflag == 'n')
  271.         newsrc_opt (lfirst,nun);
  272.  
  273.     for ( ; fret != NULL; ++line, fret = fgets(buf,RECLEN-1,fp))
  274.     {
  275.         if (emptyline(buf) == 1)
  276.             continue;
  277.         if ((ptr = strpbrk(buf,marks)) == NULL)
  278.         {
  279.             fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
  280.             line,Newsrc,buf);
  281.             continue;
  282.         }
  283.         submark = *ptr;
  284.         *ptr = '\0';
  285.         ++ptr;
  286.         num = 0;
  287.         for (ptr = strtok(ptr," ,-\n"); ptr != NULL; ptr = strtok(NULL," ,-\n"))
  288.         {
  289.             len = atoi (ptr);
  290.             for ( ; *ptr >= '0' && *ptr <= '9'; ++ptr)
  291.                 ;
  292.             if (*ptr != '\0' || len < num)
  293.             {
  294.                 num = -1;
  295.                 fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
  296.                 line,Newsrc,buf);
  297.                 break;
  298.             }
  299.             num = len;
  300.         }
  301.         if (num < 0)
  302.             continue;
  303.         chkgroup (buf,submark,num,0);
  304.     }
  305.     fclose (fp);
  306.  
  307.     /* now take care of groups not specified in .newsrc */
  308.     art_active();
  309.  
  310.     /* free up the option string storage */
  311.     for (num=0; num < Ntopt; ++num)
  312.         regfree (Topt[num]);
  313.     for (num=0; num < Nwopt; ++num)
  314.         regfree (Wopt[num]);
  315.     for (num=0; num < Nntopt; ++num)
  316.         regfree (Negtopt[num]);
  317.     for (num=0; num < Nnwopt; ++num)
  318.         regfree (Negwopt[num]);
  319.     Ntopt = Nwopt = Nntopt = Nnwopt = 0;
  320.  
  321.     /* free the active list */
  322.     free ((char *) Active);
  323. }
  324.  
  325. static
  326. emptyline(s)
  327. char *s;
  328. {
  329.     while (isspace(*s))
  330.         ++s;
  331.     if (*s == '\0')
  332.         return (1);
  333.     return (0);
  334. }
  335.  
  336. /*
  337.     fill hash table from active news group list
  338.     This is needed to be able to process options
  339.     before scanning user order.  Constructs an array
  340.     of active newsgroup names for the rest of vns_nws().
  341. */
  342. static
  343. fill_active ()
  344. {
  345.     FILE *f;
  346.     char *nread, act_rec[RECLEN], *actfile;
  347.     int num,lownum,rcount;
  348.  
  349.     Max_name = 0;
  350.     actfile = vn_env("ACTFILE", ACTFILE);
  351.     if ((f = fopen (actfile,"r")) == NULL)
  352.         printex ("couldn't open %s\n",actfile);
  353.  
  354.     /*
  355.     ** we do things this way so that we only examine active records
  356.     ** once, minimizing the window where changes could screw us up
  357.     ** at the cost of possibly alloc'ing a few extra bytes.  We start
  358.     ** with a count of one to have a positive rcount for alloc.
  359.     */
  360.     for(rcount=1; fgets(act_rec, RECLEN-1, f) != NULL; ++rcount)
  361.         ;
  362.     if ((Active = (char **) malloc(rcount*sizeof(char *))) == NULL)
  363.         printex("Memory allocation failure");
  364.     rewind(f);
  365.  
  366.     Actnum = 0;
  367.     while (Actnum < rcount && fgets(act_rec, RECLEN-1, f) != NULL)
  368.     {
  369.         if (strtok (act_rec," \n") == NULL)
  370.             continue;
  371.         nread = strtok (NULL, " \n");
  372.         if (nread != NULL)
  373.             num = atoi(nread);
  374.         else
  375.             num = 0;
  376.         nread = strtok (NULL, " \n");
  377.         if (nread != NULL)
  378.             lownum = atoi(nread);
  379.         else
  380.             lownum = 0;
  381.         if (lownum > 0)
  382.             --lownum;
  383.         if (strlen(act_rec) > Max_name)
  384.             Max_name = strlen(act_rec);
  385.         /* enter newsgroup, point to permanent copy of name */
  386.         hashenter (act_rec, num, lownum);
  387.         Active[Actnum] = (myfind(act_rec))->nd_name;
  388.         ++Actnum;
  389.     }
  390.  
  391.     fclose (f);
  392. }
  393.  
  394. /*
  395.     check active newsgroups not mentioned in NEWSRC file
  396.     (SFLG_SCAN not set)
  397. */
  398. static
  399. art_active ()
  400. {
  401.     int i;
  402.     NODE *ptr;
  403.  
  404.     for( i=0; i < Actnum; ++i)
  405.     {
  406.         ptr = myfind(Active[i]);
  407.         if ((ptr->state & SFLG_SCAN) == 0)
  408.             chkgroup (ptr->nd_name, NEWS_ON, 0, 1);
  409.     }
  410. }
  411.  
  412. /*
  413.     check group for new articles:
  414.     s - group
  415.     c - subscription indicator from NEWSRC
  416.     n - number read
  417.     new - new newsgroup flag
  418. */
  419. static
  420. chkgroup (s,c,n,new)
  421. char *s,c;
  422. int n;
  423. int new;
  424. {
  425.     NODE *ptr;
  426.     char sub;
  427.     int nrd;
  428.     int lowart;
  429.     int st;
  430.  
  431.     if ((ptr = hashfind(s)) != NULL && (ptr->state & SFLG_SCAN) == 0)
  432.     {
  433.         ptr->state |= SFLG_SCAN;
  434.  
  435. #ifdef SYN_CHECK
  436.         /* if "read" more than exist, reset */
  437.         if (n > ptr->highnum)
  438.         {
  439.             n = ptr->highnum - SYN_SETBACK;
  440.             fgprintf("%s: .newsrc out of synch, resetting\n",s);
  441.         }
  442. #endif
  443.         lowart = ptr->lownum;
  444.         if (n < ptr->lownum)
  445.             n = ptr->lownum;
  446.  
  447.         nrd = n;
  448.         sub = c;
  449.  
  450.         /*
  451.         ** scan decision is rather complex, since GF_ALL setting
  452.         ** overides "n" value, GF_SPEC indicates SFLG_SPEC flag used.
  453.         ** if GF_OVER set, SFLG_SPEC overides subscription mark, else
  454.         ** SFLG_SPEC AND subscribed is neccesary.
  455.         */
  456.         if ((Gflags & GF_SPEC) != 0)
  457.         {
  458.             if ((ptr->state & SFLG_SPEC) == 0)
  459.                 c = NEWS_OFF;
  460.             else
  461.             {
  462.                 if ((Gflags & GF_OVER) != 0)
  463.                     c = NEWS_ON;
  464.             }
  465.         }
  466.         if ((Gflags & GF_ALL) != 0)
  467.             n = lowart;
  468.         fw_group(s, new, sub == NEWS_ON, nrd, c == NEWS_ON);
  469.         if (c == NEWS_ON && ptr->highnum > n)
  470.         {
  471.             st = outgroup (s,n,ptr->highnum);
  472.             if (st > nrd)
  473.                 fw_chg(new, sub == NEWS_ON, st, c == NEWS_ON);
  474.         }
  475.     }
  476. }
  477.  
  478. /*
  479.     vns_write writes the .newsrc file
  480. */
  481. vns_write(news,ncount)
  482. NODE **news;
  483. int ncount;
  484. {
  485.     FILE *fp;
  486.     NODE *p;
  487.     char c;
  488.     int i,rc;
  489.  
  490.     if (link(Newsrc,Onews) < 0)
  491.         printex ("can't backup %s to %s before writing",Newsrc,Onews);
  492.  
  493.     if (unlink(Newsrc) < 0 || (fp = fopen(Newsrc,"w")) == NULL)
  494.         printex ("can't open %s for writing (backed up in %s)",Newsrc,Onews);
  495.     else
  496.     {
  497.         clearerr(fp);
  498.         for (i=0; (rc = ferror(fp)) == 0 && i < Optlines; ++i)
  499.             fprintf (fp,"%s",Options[i]);
  500.         for (i=0; rc == 0 && i < ncount; ++i)
  501.         {
  502.             p = news[i];
  503.             if ((p->flags & FLG_SUB) == 0)
  504.                 c = NEWS_OFF;
  505.             else
  506.                 c = NEWS_ON;
  507. #ifdef OLDRC
  508.             fprintf (fp,"%s%c %d\n",p->nd_name,c,p->rdnum);
  509. #else
  510.             if (p->rdnum > 0)
  511.                 fprintf(fp,"%s%c 1-%d\n",p->nd_name,c,p->rdnum);
  512.             else
  513.                 fprintf(fp,"%s%c 0\n",p->nd_name,c);
  514. #endif
  515.             rc = ferror(fp);
  516.         }
  517.         fclose (fp);
  518.         if (rc != 0)
  519.             printex ("write of %s failed, old copy stored in %s",Newsrc,Onews);
  520.         else
  521.             unlink (Onews);
  522.     }
  523. }
  524.  
  525. /*
  526.     arg_opt must be called prior to option scanning, since
  527.     it uses the options array.  This is a bit of a kludge,
  528.     but it saves a bunch of work.  NOTE - no command name argument
  529. */
  530. static
  531. arg_opt (argc,argv,lfirst,nun)
  532. int argc;
  533. char **argv;
  534. int *lfirst, *nun;
  535. {
  536.     if (argc > OPTLINES)
  537.         printex ("too many command line options (%d allowed)\n",OPTLINES);
  538.     for (Optlines=0; Optlines < argc; ++Optlines)
  539.     {
  540.         Options[Optlines] = *argv;
  541.         ++argv;
  542.     }
  543.     newsrc_opt(lfirst,nun);
  544. }
  545.  
  546. /*
  547.     option setting routine:
  548.     sets global flags: GF_ALL for -x option GF_SPEC for -n.
  549.     sets up filter array for article scanning
  550. */
  551. static
  552. newsrc_opt(lfirst,nun)
  553. int *lfirst, *nun;
  554. {
  555.     int i;
  556.     char curopt,tmp[RECLEN],*tok;
  557.  
  558.     *nun = *lfirst = 0;
  559.     Ntopt = Nwopt = Nnwopt = Nntopt = 0;
  560.     curopt = '\0';
  561.     for (i=0; i < Optlines; ++i)
  562.     {
  563.         strcpy(tmp,Options[i]);
  564.         for (tok = strtok(tmp,",\\ \t\n"); tok != NULL; tok = strtok(NULL,",\\ \t\n"))
  565.         {
  566.             if (*tok != '-')
  567.                 do_opt (curopt,tok);
  568.             else
  569.             {
  570.                 for (++tok; index("nwt",*tok) == NULL; ++tok)
  571.                 {
  572.                     /* options with no strings */
  573.                     switch(*tok)
  574.                     {
  575.                     case 'S':
  576.                         Gflags &= ~GF_OVER;
  577.                         break;
  578.                     case '%':
  579.                         *lfirst = 1;
  580.                         break;
  581.                     case 'U':
  582.                         *nun = 1;
  583.                         break;
  584. #ifdef OLDRC
  585.                     case 'i':
  586.                     /* Treat "-i" as synonym for "-x" */
  587. #endif
  588.                     case 'x':
  589.                         Gflags |= GF_ALL;
  590.                     default:
  591.                         break;
  592.                     }
  593.                 }
  594.                 curopt = *tok;
  595.                 if (*(++tok) != '\0')
  596.                     do_opt (curopt,tok);
  597.             }
  598.         }
  599.     }
  600. }
  601.  
  602. /* do_opt is for options with strings attached */
  603. static
  604. do_opt (opt,str)
  605. char opt, *str;
  606. {
  607.     switch (opt)
  608.     {
  609.     case 'n':
  610.         Gflags |= GF_SPEC;
  611.         specmark(str);
  612.         break;
  613.     case 'w':
  614.         specfilter (FIL_AUTHOR,str);
  615.         break;
  616.     case 't':
  617.         specfilter (FIL_TITLE,str);
  618.         break;
  619.     default:
  620. #ifdef OLDRC
  621.         Gflags |= GF_SPEC;    /* Assume anything else is newsgroup */
  622.         specmark(str);
  623. #endif
  624.         break;
  625.     }
  626. }
  627.  
  628. static
  629. specfilter (comp,str)
  630. char comp,*str;
  631. {
  632.     int *count;
  633.     char **rex;
  634.  
  635.     /*
  636.     ** we may set rex one past end of array.  we will error before
  637.     ** referencing it if that's the case, however.
  638.     */
  639.     if (*str == '!')
  640.     {
  641.         if (comp == FIL_TITLE)
  642.         {
  643.             count = &Nntopt;
  644.             rex = Negtopt + *count;
  645.         }
  646.         else
  647.         {
  648.             count = &Nnwopt;
  649.             rex = Negwopt + *count;
  650.         }
  651.         ++str;
  652.     }
  653.     else
  654.     {
  655.         if (comp == FIL_TITLE)
  656.         {
  657.             count = &Ntopt;
  658.             rex = Topt + *count;
  659.         }
  660.         else
  661.         {
  662.             count = &Nwopt;
  663.             rex = Wopt + *count;
  664.         }
  665.     }
  666.     if (*count >= NUMFILTER)
  667.         printex ("too many %c options, %d allowed",comp,NUMFILTER);
  668.     if ((*rex = regcmp(str,(char *) 0)) == NULL)
  669.         printex ("%c option regular expression syntax: %s",comp,str);
  670.     ++(*count);
  671. }
  672.  
  673. /*
  674.     handle the newsgroup specification string.
  675.     ("all" convention - braack!!!)
  676. */
  677. static
  678. void
  679. specmark (s)
  680. char *s;
  681. {
  682.     unsigned ormask,andmask;
  683.     int i,len;
  684.     char *ptr,*re,pattern[RECLEN];
  685.     NODE *nptr;
  686.  
  687.     if (*s == '!')
  688.     {
  689.         ++s;
  690.         ormask = 0;
  691.         andmask = ~SFLG_SPEC;
  692.         if (*s == '\0')
  693.             return;
  694.     }
  695.     else
  696.     {
  697.         ormask = SFLG_SPEC;
  698.         andmask = 0xffff;
  699.     }
  700.  
  701.     /* convert "all" not bounded by alphanumerics to ".*". ".all" becomes ".*" */
  702.     for (ptr = s; (len = findall(ptr)) >= 0; ptr += len+1)
  703.     {
  704.         if (len > 0 && isalnum (s[len-1]))
  705.             continue;
  706.         if (isalnum (s[len+3]))
  707.             continue;
  708.         if (len > 0 && s[len-1] == '.')
  709.         {
  710.             --len;
  711.             strcpy (s+len,s+len+1);
  712.         }
  713.         s[len] = '.';
  714.         s[len+1] = '*';
  715.         strcpy (s+len+2,s+len+3);
  716.     }
  717.  
  718.     /* now use regular expressions */
  719.     sprintf (pattern,"^%s$",s);
  720.     if ((re = regcmp(pattern,(char *) 0)) == NULL)
  721.         printex ("n option regular expression syntax: %s",s);
  722.     for (i=0; i < Actnum; ++i)
  723.     {
  724.         nptr = myfind(Active[i]);
  725.         if (regex(re,nptr->nd_name) != NULL)
  726.         {
  727.             nptr->state |= ormask;
  728.             nptr->state &= andmask;
  729.         }
  730.     }
  731.     regfree (re);
  732. }
  733.  
  734. static
  735. findall (s)
  736. char *s;
  737. {
  738.     int len;
  739.     for (len=0; *s != '\0'; ++s,++len)
  740.     {
  741.         if (*s == 'a' && strncmp(s,"all",3) == 0)
  742.             return (len);
  743.     }
  744.     return (-1);
  745. }
  746.  
  747. static
  748. grp_indic (s,ok)
  749. char *s;
  750. int ok;
  751. {
  752.     if (ok)
  753.         fgprintf("    %s\n",s);
  754.     else
  755.         fgprintf("    %s - Can't access spool directory\n",s);
  756. }
  757.  
  758. /*
  759.     enter newsgroup articles.
  760.     all articles between low and hi are to be included.
  761.  
  762.     Returns the highest number less than an OPENED (not neccesarily
  763.     accepted) article to allow caller to revise "articles read"
  764.     number beyond non-existent articles.
  765. */
  766. outgroup (s,low,hi)
  767. char *s;
  768. int low,hi;
  769. {
  770.     int i;
  771.     char subj[RECLEN], lines[RECLEN], auth[RECLEN], gd[RECLEN];
  772.     int ret,op;
  773.  
  774.     if ((hi-low) > MAXARTRANGE)
  775.         low = hi - MAXARTRANGE;
  776.  
  777.     ret = low;
  778.     op = 1;
  779.  
  780.     g_dir(s,gd);
  781.     if (chdir(gd) < 0)
  782.     {
  783.         grp_indic(s,0);
  784.         return (ret);
  785.     }
  786.     grp_indic(s,1);
  787.     for (i=low+1; i <= hi; ++i)
  788.     {
  789.         if (digname(i,subj,lines,auth,&op) >= 0)
  790.         {
  791.             fw_art(i,subj,lines,auth);
  792.         }
  793.         else
  794.         {
  795.             if (op)
  796.                 ret = i;
  797.         }
  798.     }
  799.  
  800.     return(ret);
  801. }
  802.  
  803. /*
  804. ** open article and interpret options, if any.  The op parameter is set
  805. ** to ZERO if and only if an article is opened.  Used above as a flag to
  806. ** indicate no articles opened yet.
  807. */
  808. static digname (n, subj, lines, auth, op)
  809. int n;
  810. char *subj, *lines, *auth;
  811. int *op;
  812. {
  813.     int i,j;
  814.     FILE *fp;
  815.     char t[RECLEN];
  816.  
  817.     /* open article */
  818.     sprintf (t,"%d", n);
  819.     if ((fp = fopen(t,"r")) == NULL)
  820.         return (-1);
  821.     *op = 0;
  822.  
  823.     /* get subject, from and lines by reading article */
  824.     subj[0] = lines[0] = auth[0] = '?';
  825.     subj[1] = lines[1] = auth[1] = '\0';
  826.     for (i = 0; i < HDR_LINES && nfgets(t,RECLEN-1,fp) != NULL; ++i)
  827.     {
  828.         if (index(CHFIRST,t[0]) == NULL)
  829.             continue;
  830.         t[strlen(t) - 1] = '\0';
  831.         if (strncmp(T_head,t,THDLEN) == 0)
  832.         {
  833.             for (j=0; j < Nntopt; ++j)
  834.             {
  835.                 if (regex(Negtopt[j],t+THDLEN) != NULL)
  836.                 {
  837.                     fclose(fp);
  838.                     return(-1);
  839.                 }
  840.             }
  841.             if (Ntopt > 0)
  842.             {
  843.                 for (j=0; j < Ntopt; ++j)
  844.                 {
  845.                     if (regex(Topt[j],t+THDLEN) != NULL)
  846.                         break;
  847.                 }
  848.                 if (j >= Ntopt)
  849.                 {
  850.                     fclose(fp);
  851.                     return(-1);
  852.                 }
  853.             }
  854.             strcpy(subj,t+THDLEN);
  855.             continue;
  856.         }
  857.         if (strncmp(F_head,t,FHDLEN) == 0)
  858.         {
  859.             for (j=0; j < Nnwopt; ++j)
  860.             {
  861.                 if (regex(Negwopt[j],t+FHDLEN) != NULL)
  862.                 {
  863.                     fclose(fp);
  864.                     return(-1);
  865.                 }
  866.             }
  867.             if (Nwopt > 0)
  868.             {
  869.                 for (j=0; j < Nwopt; ++j)
  870.                 {
  871.                     if (regex(Wopt[j],t+FHDLEN) != NULL)
  872.                         break;
  873.                 }
  874.                 if (j >= Nwopt)
  875.                 {
  876.                     fclose(fp);
  877.                     return(-1);
  878.                 }
  879.             }
  880.             strcpy(auth,t+FHDLEN);
  881.             continue;
  882.         }
  883.         if (strncmp(L_head,t,LHDLEN) == 0)
  884.         {
  885.             strcpy(lines,t+LHDLEN);
  886.             break;
  887.         }
  888.     }
  889.  
  890.     fclose (fp);
  891.  
  892.     /* reject empty or 1 line files */
  893.     if (i < 2)
  894.         return (-1);
  895.  
  896.     return (0);
  897. }
  898.  
  899. /*
  900. ** special fgets for reading header lines, which unfolds continued lines
  901. ** and throws away trailing stuff on buffer overflow.
  902. */
  903. static char *
  904. nfgets(buf, size, fp)
  905. char    *buf;
  906. int    size;
  907. FILE    *fp;
  908. {
  909.     register int c;
  910.  
  911.     while (!feof(fp))
  912.     {
  913.         if ((c = getc(fp)) == '\n')
  914.         {
  915.             if ((c = getc(fp)) == '\t' || c == ' ')
  916.                 continue;
  917.             ungetc(c, fp);
  918.             *buf = '\n';
  919.             ++buf;
  920.             *buf = '\0';
  921.             ++buf;
  922.             return (buf);
  923.         }
  924.  
  925.         /* prevent "terminal bombs" */
  926.         if (c < ' ' || c == '\177')
  927.         {
  928.             switch(c)
  929.             {
  930.             case '\r':
  931.             case '\010':
  932.             case '\07':
  933.                 break;
  934.             case '\177':
  935.                 c = '~';
  936.                 break;
  937.             case '\t':
  938.                 c = ' ';
  939.                 break;
  940.             default:
  941.                 if (size > 1)
  942.                 {
  943.                     *buf = '^';
  944.                     ++buf;
  945.                     --size;
  946.                 }
  947.                 c += 'A' - 1;
  948.                 break;
  949.             }
  950.         }
  951.  
  952.         if (size > 0)
  953.         {
  954.             *buf = c;
  955.             ++buf;
  956.             --size;
  957.         }
  958.         if (c == '\r')
  959.         {
  960.             if ((c = getc(fp)) != '\n')
  961.             {
  962.                 ungetc(c, fp);
  963.                 continue;
  964.             }
  965.             if ((c = getc(fp)) != ' ' && c != '\t')
  966.             {
  967.                 *buf = '\0';
  968.                 ++buf;
  969.                 ungetc(c, fp);
  970.                 return (buf);
  971.             }
  972.             --buf;
  973.             ++size;
  974.             continue;
  975.         }
  976.     }
  977.  
  978.     *buf = '\0';
  979.     ++buf;
  980.     return (NULL);
  981. }
  982.  
  983. static char *Mail[2], *Show[6], *Post[4];
  984. static char *Priv[8];
  985. static char *Pool = NULL;
  986.  
  987. FILE *
  988. vns_aopen(art,hdr)
  989. int art;
  990. ARTHEADER *hdr;
  991. {
  992.     char buf[RECLEN];
  993.     char *dist, *reply, *from, *ngrp, *flto, *path, *resubj;
  994.     FILE *fp;
  995.     int n;
  996.  
  997.     dist = resubj = path = reply = from = ngrp = flto = NULL;
  998.  
  999.     sprintf(buf,"%d",art);
  1000.     if ((fp = fopen(buf,"r")) == NULL)
  1001.         return(NULL);
  1002.  
  1003.     /*
  1004.     ** we only really need a lot extra if MAILCHOOSE, but allocating
  1005.     ** a temporary array of pointers isn't that much.  Similarly, a
  1006.     ** few assignments, and the "Priv" declaration are only needed
  1007.     ** with some define settings.  Not worth ifdef'ing.
  1008.     */
  1009.     Pool = str_tpool(100);
  1010.  
  1011.     hdr->artid = "<some article>";
  1012.     hdr->from = "<somebody>";
  1013.     hdr->priv = Priv;
  1014.     hdr->postcmd = Poster;
  1015.     hdr->mail = Mail;
  1016.     hdr->show = Show;
  1017.     hdr->post = Post;
  1018.     hdr->priv_num = hdr->show_num = hdr->post_num = hdr->mail_num = 0;
  1019.  
  1020.     /* for conditional is abnormal - expected exit is break */
  1021.     for (n=0; n < HDR_LINES && fgets(buf,RECLEN-1,fp) != NULL; ++n)
  1022.     {
  1023.         /* bail out at first non-header line */
  1024.         if (buf[0] == '\n')
  1025.             break;
  1026.         if (strncmp(buf,RT_head,RTHDLEN) == 0)
  1027.         {
  1028.             buf [strlen(buf)-1] = '\0';
  1029.             reply = str_tstore(Pool,buf+RTHDLEN);
  1030.             continue;
  1031.         }
  1032.         if (strncmp(buf,P_head,PHDLEN) == 0)
  1033.         {
  1034.             buf [strlen(buf)-1] = '\0';
  1035.             path = str_tstore(Pool,buf+PHDLEN);
  1036.             continue;
  1037.         }
  1038.         if (strncmp(buf,DIS_head,DISHDLEN) == 0)
  1039.         {
  1040.             buf [strlen(buf)-1] = '\0';
  1041.             dist = str_tstore(Pool,buf);
  1042.             continue;
  1043.         }
  1044.         if (strncmp(buf,M_head,MHDLEN) == 0)
  1045.         {
  1046.             buf [strlen(buf)-1] = '\0';
  1047.             hdr->artid = str_tstore(Pool,buf+MHDLEN);
  1048.             continue;
  1049.         }
  1050.         if (strncmp(buf,F_head,FHDLEN) == 0)
  1051.         {
  1052.             buf [strlen(buf)-1] = '\0';
  1053.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1054.             from = hdr->from = (hdr->show)[hdr->show_num]+FHDLEN;
  1055.             ++(hdr->show_num);
  1056.             continue;
  1057.         }
  1058.         if (strncmp(buf,T_head,THDLEN) == 0)
  1059.         {
  1060.             buf [strlen(buf)-1] = '\0';
  1061.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1062.             if (strncmp(buf+THDLEN,Fpfix,FPFLEN) != 0)
  1063.             {
  1064.                 sprintf(buf,"%s%s%s",T_head,Fpfix,
  1065.                     ((hdr->show)[hdr->show_num])+THDLEN);
  1066.                 resubj = str_tstore(Pool,buf);
  1067.             }
  1068.             else
  1069.                 resubj = (hdr->show)[hdr->show_num];
  1070.             ++(hdr->show_num);
  1071.             continue;
  1072.         }
  1073.         if (strncmp(buf,N_head,NHDLEN) == 0)
  1074.         {
  1075.             buf [strlen(buf)-1] = '\0';
  1076.  
  1077.             /* if multiple newsgroups, include in "show" */
  1078.             if (index(buf,',') != NULL)
  1079.             {
  1080.                 (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1081.                 ngrp = (hdr->show)[hdr->show_num] + NHDLEN;
  1082.                 ++(hdr->show_num);
  1083.             }
  1084.             else
  1085.                 ngrp = str_tstore(Pool,buf+NHDLEN);
  1086.             continue;
  1087.         }
  1088.         if (strncmp(buf,FT_head,FTHDLEN) == 0)
  1089.         {
  1090.             buf [strlen(buf)-1] = '\0';
  1091.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1092.             flto = (hdr->show)[hdr->show_num] + FTHDLEN;
  1093.             ++(hdr->show_num);
  1094.             continue;
  1095.         }
  1096.         if (strncmp(buf,L_head,LHDLEN) == 0)
  1097.         {
  1098.             buf [strlen(buf)-1] = '\0';
  1099.             hdr->lines = atoi(buf+LHDLEN);
  1100.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1101.             ++(hdr->show_num);
  1102.             continue;
  1103.         }
  1104.     }
  1105.  
  1106.     hdr->hlines = n;
  1107.  
  1108. #ifdef MAILCHOOSE
  1109.     (hdr->priv)[hdr->priv_num] = resubj;
  1110.     ++(hdr->priv_num);
  1111.     if (reply != NULL)
  1112.     {
  1113.         (hdr->priv)[hdr->priv_num] = mail_trim(reply);
  1114.         ++(hdr->priv_num);
  1115.     }
  1116.     if (from != NULL)
  1117.     {
  1118.         (hdr->priv)[hdr->priv_num] = mail_trim(from);
  1119.         ++(hdr->priv_num);
  1120.     }
  1121.     if (path != NULL)
  1122.     {
  1123.         (hdr->priv)[hdr->priv_num] = mail_trim(path);
  1124.         ++(hdr->priv_num);
  1125.     }
  1126. #else
  1127. #ifdef MAILSMART
  1128.     if (reply == NULL)
  1129.         if (from != NULL)
  1130.             reply = from;
  1131.         else
  1132.         {
  1133.             if (path != NULL)
  1134.                 reply = path;
  1135.         }
  1136. #else
  1137.     if (path != NULL)
  1138.         reply = path;
  1139. #endif
  1140.     if (reply != NULL)
  1141.            reply =  mail_trim(reply);
  1142.     mail_cmd(hdr,reply,resubj);
  1143. #endif /* MAILCHOOSE */
  1144.  
  1145.     if (flto == NULL)
  1146.     {
  1147.         if ((flto = ngrp) == NULL)
  1148.             flto = "group.unknown";
  1149.     }
  1150.     ngrp = rindex(flto,'.');
  1151.  
  1152.     if (strncmp("mod.",flto,4) == 0 ||
  1153.             (ngrp != NULL && strcmp(".announce",ngrp) == 0))
  1154.     {
  1155.         sprintf(buf,"Cannot post a follow-up to \"%s\", reply with mail to moderator",flto);
  1156.         hdr->post_err = str_tstore(Pool,buf);
  1157.         return (fp);
  1158.     }
  1159.  
  1160.     if (ngrp != NULL && strcmp(ngrp,".general") == 0)
  1161.     {
  1162.         *ngrp = '\0';
  1163.         sprintf(buf,"%s%s.followup",N_head,flto);
  1164.     }
  1165.     else
  1166.         sprintf(buf,"%s%s",N_head,flto);
  1167.     flto = str_tstore(Pool,buf);
  1168.  
  1169.     hdr->post_err = NULL;
  1170.  
  1171.     if (resubj != NULL)
  1172.     {
  1173.         (hdr->post)[hdr->post_num] = resubj;
  1174.         ++(hdr->post_num);
  1175.     }
  1176.  
  1177.     (hdr->post)[hdr->post_num] = flto;
  1178.     ++(hdr->post_num);
  1179.  
  1180.     sprintf(buf,"%s%s",R_head,hdr->artid);
  1181.     (hdr->post)[hdr->post_num] = str_tstore(Pool,buf);
  1182.     ++(hdr->post_num);
  1183.  
  1184.     if (dist != NULL)
  1185.     {
  1186.         (hdr->post)[hdr->post_num] = dist;
  1187.         ++(hdr->post_num);
  1188.     }
  1189.  
  1190.     return (fp);
  1191. }
  1192.  
  1193. #ifdef MAILCHOOSE
  1194. /*
  1195. ** routine to prompt user for mail path approval
  1196. */
  1197. static
  1198. mail_prompt(hdr)
  1199. ARTHEADER *hdr;
  1200. {
  1201.     int i;
  1202.     char buf[RECLEN],*ptr;
  1203.  
  1204.     tty_set(SAVEMODE);
  1205.     for (i=1; i < hdr->priv_num; ++i)
  1206.     {
  1207. #ifdef amiga
  1208.         sprintf(pr_buf,"%d - %s\n",i,(hdr->priv)[i]);
  1209.         tputs(pr_buf,1,ttputc);
  1210. #else
  1211.         printf("%d - %s\n",i,(hdr->priv)[i]);
  1212. #endif
  1213.     }
  1214. #ifdef amiga
  1215.     sprintf(pr_buf,"\nType number to choose one of the above, or input address: ");
  1216.     tputs(pr_buf,1,ttputc);
  1217. #else
  1218.     printf("\nType number to choose one of the above, or input address: ");
  1219. #endif
  1220.     fgets(buf,RECLEN-1,stdin);
  1221.     tty_set(RESTORE);
  1222.  
  1223.     ptr = strtok(buf," \t\n");
  1224.     if (ptr == NULL)
  1225.         ptr = "";
  1226.  
  1227.     i = strlen(ptr);
  1228.     if (i == 1)
  1229.     {
  1230.         i = atoi(ptr);
  1231.         if (i > 0 && i <= hdr->priv_num)
  1232.             ptr = (hdr->priv)[i];
  1233.         i = 1;
  1234.     }
  1235.  
  1236.     /*
  1237.     ** If the user keeps cycling through here on the same article,
  1238.     ** we will eventually run out of strings.  We made Pool large
  1239.     ** enough to make it unlikely (user will have to retry about 80
  1240.     ** times without switching articles).  Hardly elegant, but should
  1241.     ** be sufficient.
  1242.     */
  1243.     if (i > 1 && hdr->priv_num < 8)
  1244.     {
  1245.         (hdr->priv)[hdr->priv_num] = str_tstore(Pool,ptr);
  1246.         ++(hdr->priv_num);
  1247.     }
  1248.     mail_cmd(hdr,ptr,(hdr->priv)[0]);
  1249. }
  1250. #endif
  1251.  
  1252. /*
  1253. ** trim () off potential mail address, and make copy if needed.
  1254. ** addr must be allocated string.
  1255. */
  1256. static char *
  1257. mail_trim(addr)
  1258. char *addr;
  1259. {
  1260.     char buf[RECLEN];
  1261.     char *ptr;
  1262.  
  1263.     if (index(addr,'(') == NULL)
  1264.         return(addr);
  1265.  
  1266.     strcpy(buf,addr);
  1267.     ptr = index(buf,'(');
  1268.     for (--ptr; *ptr == ' ' || *ptr == '\t'; --ptr)
  1269.         ;
  1270.     ++ptr;
  1271.     *ptr = '\0';
  1272.     return (str_tstore(Pool,buf));
  1273. }
  1274.  
  1275. /*
  1276. ** format mail command.  Subj must point to allocated string.
  1277. */
  1278. static
  1279. void
  1280. mail_cmd(hdr,addr,subj)
  1281. ARTHEADER *hdr;
  1282. char *addr, *subj;
  1283. {
  1284.     char buf[RECLEN];
  1285.  
  1286.     if (addr == NULL || *addr == '\0')
  1287.     {
  1288.         hdr->mail_err = "No address";
  1289.         return;
  1290.     }
  1291.  
  1292.     hdr->mail_err = NULL;
  1293.             ;
  1294.  
  1295. #ifdef INLETTER
  1296.     hdr->mailcmd = Mailer;
  1297.     sprintf(buf,"%s%s",TO_head,addr);
  1298.     (hdr->mail)[0] = str_tstore(Pool,buf);
  1299.     hdr->mail_num = 1;
  1300. #else
  1301.     sprintf(buf,Mailer,addr);
  1302.     hdr->mailcmd = str_tstore(Pool,buf);
  1303.     hdr->mail_num = 0;
  1304. #endif
  1305.     if (subj != NULL)
  1306.     {
  1307.         (hdr->mail)[hdr->mail_num] = subj;
  1308.         ++(hdr->mail_num);
  1309.     }
  1310. }
  1311.  
  1312. vns_aclose(fp)
  1313. FILE *fp;
  1314. {
  1315.      if (Pool != NULL)
  1316.          str_tfree(Pool);
  1317.      Pool = NULL;
  1318.     fclose(fp);
  1319. }
  1320.  
  1321. /*
  1322. ** we don't use the count / name / mode arguments because this doesn't
  1323. ** implement any fancy article massaging
  1324. */
  1325. void
  1326. vns_asave(art,fp)
  1327. int art;
  1328. FILE *fp;
  1329. {
  1330.     char buf[RECLEN];
  1331.     FILE *fin;
  1332.  
  1333.     sprintf(buf,"%d",art);
  1334.     if ((fin = fopen(buf,"r")) == NULL)
  1335.         return;
  1336.  
  1337.     while (fgets(buf,RECLEN-1,fin) != NULL)
  1338.         fputs(buf,fp);
  1339.     fclose(fin);
  1340. }
  1341.  
  1342. vns_exit()
  1343. {
  1344. }
  1345.